package com.lx.seckill.service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lx.seckill.mapper.TseckillProductMapper;
import com.lx.seckill.pojo.TSeckillProduct;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * TODO 添加描述
 *
 * @author wangLJ
 * @date 2024/7/18 11:33
 */
@Service
public class TseckillProductServiceImpl extends ServiceImpl<TseckillProductMapper, TSeckillProduct> implements TseckillProductService {
    @Autowired
    TseckillProductMapper mapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private ThreadPoolTaskExecutor executor;
    @Resource
    RedissonClient redissonClient;

    @Override
    public List<TSeckillProduct> list() {

        return super.list();
    }

    @Override
    public TSeckillProduct byid() {
        TSeckillProduct tSeckillProduct = mapper.selectById(2);
        return tSeckillProduct;
    }

    @Transactional
    @Override
    public void updateStockCount() {

        /**
         * 获取一把锁,只要锁的名字一样,就是同一把锁，"my-lock"是锁名，也是Redis的哈希模型的对外key
         */
        RLock lock = redissonClient.getLock("my-lock");
        //加锁
        /**
         * 阻塞式等待,默认加的锁等待时间为30s。每到20s(经过三分之一看门狗时间后)就会自动续借成30s
         * 1.锁的自动续期,如果在业务执行期间业务没有执行完成,redisson会为该锁自动续期
         * 2.加锁的业务只要运行完成,就不会自动续期,即使不手动解锁,锁在默认的30s后会自动删除
         */
        // lock.lock();
        /**
         * （推荐）指定锁的过期时间，看门狗不会自动续期：
         * 在自定义锁的存在时间时不会自动解锁
         * 注意：
         * 设置的自动解锁时间一定要稳稳地大于业务时间
         */
        lock.lock(30, TimeUnit.SECONDS);

        try {
            TSeckillProduct tSeckillProduct = mapper.selectById(2);
            if (tSeckillProduct.getStockCount() > 0) {
                //扣减库存
                mapper.updateStockCount();
            }
        } finally {
            //释放锁
            lock.unlock();
        }

    }


    @Override
    public String putIfAbsent() {
        /**
         * opsForHash().putIfAbsent() 方法的作用是在哈希表中设置字段的值，
         * 但仅当字段不存在时才会设置成功。如果字段已经存在，这个方法不会更新现有的值，而是保持当前的值不变。
         */
        TSeckillProduct tSeckillProduct = mapper.selectById(2);
        Integer stockCount = tSeckillProduct.getStockCount();
        /**
         * 应该使用putIfAbsent 方法
         * redisTemplate.opsForHash().putIfAbsent("product:stock", "quantity", stockCount+"");
         * 也可以使用
         *  redisTemplate.opsForValue().setIfAbsent("product:stock", stockCount + "");
         * 测试为了方便设置库存，从而采用put 方法
         */
        //redisTemplate.opsForHash().putIfAbsent("product:stock", "quantity", stockCount+"");
        redisTemplate.opsForHash().put("product:stock", "quantity", stockCount + "");


        return "库存" + stockCount;
    }



    @Override
    public String preheat() {

        Long result = redisTemplate.opsForHash().increment("product:stock", "quantity", -1);
        System.out.println(result);

        if (result >= 0) {
            //扣减库存
            mapper.updateStockCount();
            return result + "";
        } else {
            return "库存不足";
        }
    }

    @Async("threadPoolTaskExecutor") // 指定使用哪个TaskExecutor
    @Override
    public CompletableFuture<String> asynPreheat() {
        return CompletableFuture.supplyAsync(() -> {
            String threadName = Thread.currentThread().getName();
            System.out.println("阿里 - " +threadName);
            TSeckillProduct tSeckillProduct = mapper.selectById(2);
            mapper.selectById(2);
            mapper.selectById(2);

            Long result = redisTemplate.opsForHash().increment("product:stock", "quantity", -1);
            // 模拟一个耗时操作
            if (result >= 0) {
                //扣减库存
                mapper.updateStockCount();
                return result + "";
            } else {
                return "库存不足";
            }
        }, executor);
    }

}
